﻿// Author: Hristo Hristov
// Date: 10.08.11
// Revision 5

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;

namespace NOVA.Scenery
{
    /// <summary>
    /// This class can be used to create groups of scene objects
    /// and perform uniform transformations to the group members.
    /// </summary>
    public class Controller : ITransformable
    {
        /// <summary>
        /// Different pivot points for the rotation
        /// </summary>
        public enum Pivot
        {
            /// <summary>
            /// The rotation pivot is the object center. 
            /// The object is rotated in his local coordinate system.
            /// </summary>
            Object = 0,

            /// <summary>
            /// The rotation pivot is the controller center.
            /// The objects are rotated around this center.
            /// </summary>
            Center
        }

        #region Members

        List<ITransformable> m_children;

        int m_iID;

        Vector3 m_position;
        Quaternion m_rotation;
        Vector3 m_scale;
        BoundingBox m_boundingBox;
        string m_strType;
        string m_strName;
        bool m_bHasController;
        Matrix m_ControllerMatrix;
        Vector3 m_deltaPos;
        Quaternion m_deltaOri;
        bool m_bContainsControllers;
        Pivot m_activePoint;

        #endregion

        #region Properties


        /// <summary>
        /// Gets the ID of the controller.
        /// </summary>
        public int ID
        {
            get { return m_iID; }
            internal set { m_iID = value; }
        }


        /// <summary>
        /// Gets a list of all scene objects contained by this controller.
        /// </summary>
        public List<ITransformable> Children
        {
            get { return m_children; }
        }

        /// <summary>
        /// Gets or sets the current position of the controller.
        /// </summary>
        public Vector3 Position
        {
            get
            {
                return m_position;
            }
            set
            {
                m_deltaPos = value - m_position;
                m_position = value;
            }
        }

        /// <summary>
        /// Gets or sets the current orientation of the controller.
        /// </summary>
        public Quaternion Orientation
        {
            get
            {
                return m_rotation;
            }
            set
            {
                m_deltaOri = value;// *m_rotation;
                m_rotation = value;
            }
        }

        /// <summary>
        /// Gets or sets the current position of the controller.
        /// </summary>
        public Vector3 Scale
        {
            get { return m_scale; }
            set { m_scale = value; }
        }

        public string Type
        {
            get { return m_strType; }
            set { m_strType = value; }
        }

        /// <summary>
        /// Gets or sets the current name of the controller.
        /// </summary>
        public string Name
        {
            get { return m_strName; }
            set { m_strName = value; }
        }

        /// <summary>
        /// Gets the current bounding box of the controller.
        /// </summary>
        public BoundingBox BoundingBox
        {
            get { return m_boundingBox; }
        }

        public Matrix ControllerMatrix
        {
            get
            {
                return m_ControllerMatrix;
            }
            set
            {
                m_ControllerMatrix = value;
            }
        }

        public bool HasController
        {
            get
            {
                return m_bHasController;
            }
            set
            {
                m_bHasController = value;
            }
        }

        /// <summary>
        /// Gets the current difference between the new and the old position of the controller.
        /// </summary>
        public Vector3 DeltaPos 
        { 
            get { return m_deltaPos; }
            internal set { m_deltaPos = value; }
        }

        /// <summary>
        /// Gets the current difference between the new and the old orientation of the controller.
        /// </summary>
        public Quaternion DeltaOri
        {
            get { return m_deltaOri; }
            internal set { m_deltaOri = value; }
        }

        /// <summary>
        /// Gets or sets the current pivot point of the controller. Choose between 'Object' to transform 
        /// each object individually and 'Center' to transform all objects of the group around a common center.
        /// </summary>
        public Pivot ActivePivot
        {
            get { return m_activePoint; }
            set { m_activePoint = value; }
        }

        #endregion

        #region Contructors

        /// <summary>
        /// This class can be used to create groups of scene objects
        /// and perform uniform transformations to the group members.
        /// </summary>
        public Controller(Vector3 position, Pivot pivotPoint)
            : this(position)
        {
            m_activePoint = pivotPoint;
        }

        /// <summary>
        /// This class can be used to create groups of scene objects
        /// and perform uniform transformations to the group members.
        /// </summary>
        public Controller(Vector3 position)
            : base()
        {
            m_position = position;

            m_scale = Vector3.One;
            m_rotation = Quaternion.Identity;

            m_children = new List<ITransformable>();

            Type = "Controller";

            m_iID = Core.GetNextNodeID();

            m_boundingBox = new BoundingBox(-Vector3.One * 0.5f, Vector3.One * 0.5f);

            m_deltaPos = Vector3.Zero;
            m_deltaOri = Quaternion.Identity;

            m_bContainsControllers = false;

            m_activePoint = Pivot.Object;
        }

        #endregion

        /// <summary>
        /// Adds the given object to the controller
        /// </summary>
        /// <param name="obj"></param>
        public void Add(ITransformable obj)
        {
            if (m_children.Contains(obj))
            {
                throw new Exception("This controller is already the parent of this object!");
            }

            if (obj.HasController)
            {
                throw new Exception("This object is already assigned to a controller!");
            }

            m_children.Add(obj);

            // If another controller is added as a child to this one,
            // this has to be considered during the processing of the 
            // controllers in the scene.
            m_bContainsControllers = obj is Controller;

            obj.HasController = true;
        }
    }
}
